#include "ip_handle.h"
#include <net/ethernet.h>    // struct ether_header
#include <netinet/ip.h>      // struct iphdr
#include <stdio.h>           // NULL
#include <netinet/ip_icmp.h> // struct icmphdr
#include <string.h>          // memcpy
#include <stdlib.h>          // free
#include <unistd.h>          // usleep
#include "arp_handle.h"
#include "filter.h"
/*******************************************************
函数: void *ip_recv(void *arg)
功能: 接收ip报文
参数: typedef struct MSG
    {
        int fd;                          // 描述符
        unsigned char data[MAXDATASIZE]; // 原始报文数据
    } raw_msg;
返回值: 无
********************************************************/
void *ip_recv(void *arg)
{
    raw_msg *msg = (raw_msg *)arg;
    // 广播数据一律不转发
    if (msg->data[33] == 255) // ip报文 目的ip的最后一位255代表广播
    {
        free(arg);
        return NULL;
    }

    // 获取mac报文
    struct ether_header *eth_hdr = (struct ether_header *)(msg->data);

    // 获取ip报文
    struct iphdr *ip_hdr = (struct iphdr *)(msg->data + 14);

    // 记录ip与mac映射关系(arp表)
    insert_arp_list((unsigned char *)&ip_hdr->saddr, eth_hdr->ether_shost);

    // 判断ip是否在黑名单
    if (find_filter_node(ip_hdr->saddr) != NULL) // 黑名单数据不做转发
    {
        free(arg);
        return NULL;
    }

    // 判断目的ip是否是本机(本机无需处理系统会自动处理)
    if (is_local((unsigned char *)&ip_hdr->daddr) == 0) // 如果目的ip是本机只处理icmp请求
    {
        free(arg);
        return NULL;
    }

    /* 转发报文 */
    // 判断目的ip是否在网段中
    INTERFACE *dst_eth = find_ether((unsigned char *)&ip_hdr->daddr);
    if (NULL == dst_eth) // 不在网段中则抛弃
    {
        free(arg);
        return NULL;
    }

    // 如果时回环地址不做任何处理，转发会死机
    if (strcmp(dst_eth->name, "lo") == 0)
    {
        free(arg);
        return NULL;
    }

    // 转发前查看目的ip的mac是否存在，不存在发送arp请求获取
    arp_node *ip_mac = NULL;
    for (int i = 0; i < 3; i++) // 可能一次arp请求获取不到多尝试几次
    {
        ip_mac = find_arp_ip((unsigned char *)&ip_hdr->daddr);
        if (ip_mac == NULL) // 未查询到mac
        {
            unsigned char dst_mac[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
            arp_send(msg->fd, 1, (unsigned char *)&ip_hdr->daddr, dst_mac, dst_eth); // 发送arp报文获取目的mac
        }
        else
            break;
        usleep(10); // 单位毫秒
    }
    if (ip_mac != NULL) // 获取到目的mac才会发送数据
    {
        memcpy(eth_hdr->ether_dhost, ip_mac->mac, 6);  // 修改目的mac
        memcpy(eth_hdr->ether_shost, dst_eth->mac, 6); // 修改源mac(本机mac)

        // 转发
        send_msg(msg->fd, msg->data, msg->data_len, dst_eth->name);
    }

    free(arg); // 释放资源
    return NULL;
}

/************************************************************************************************************************
函数: int icmp_send(int sockfd, unsigned char *buf, int buf_len, unsigned char dst_ip[4], unsigned char dst_mac[6], INTERFACE *eth)
功能: 接收ip报文
参数: socket文件描述符 原始报文 报文长度 目的ip 目的mac 本机网卡信息
返回值: 失败返回-1 成功返回发送字节数
*************************************************************************************************************************/
int icmp_send(int sockfd, unsigned char *buf, int buf_len, unsigned char dst_ip[4], unsigned char dst_mac[6], INTERFACE *eth)
{
    // 构建mac头部
    struct ether_header *eth_hdr = (struct ether_header *)buf;
    memcpy(eth_hdr->ether_dhost, dst_mac, 6);  // 目的mac
    memcpy(eth_hdr->ether_shost, eth->mac, 6); // 源mac(本机mac)

    // 构建ip报文
    struct iphdr *ip_hdr = (struct iphdr *)(buf + 14);
    ip_hdr->ttl--;
    ip_hdr->saddr = *(u_int32_t *)eth->ip; // 源ip
    ip_hdr->daddr = *(u_int32_t *)dst_ip;  // 目的ip
    // 开始首部校验
    ip_hdr->check = checksum((unsigned short *)ip_hdr, (ip_hdr->ihl * 4));

    // 构造ICMP报文头部
    struct icmphdr *icmp_hdr = (struct icmphdr *)(buf + 14 + (ip_hdr->ihl * 4));
    icmp_hdr->type = 1;
    icmp_hdr->un.echo.id = getpid(); // 标志
    icmp_hdr->un.echo.sequence++;    // 序号
    // 校验和
    icmp_hdr->checksum = checksum((unsigned short *)icmp_hdr, sizeof(struct icmphdr));

    return send_msg(sockfd, buf, buf_len, eth->name);
}